/*
 * Galaxium Messenger
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Text;
using System.Collections.Generic;

using Mono.Addins;
using Galaxium.Core;
using Galaxium.Protocol;

namespace Galaxium.Client
{
	/// <summary>
	/// Utility used to control input command handlers and provide an entry
	/// point for processing input commands themselves.
	/// </summary>
	public static class CommandInputHandlerUtility
	{
		private static Dictionary<IProtocol, InputCommandCollection> _protocolSpecificCommands;
		private static InputCommandCollection _allProtocolCommands;

		static CommandInputHandlerUtility ()
		{
			_protocolSpecificCommands = new Dictionary<IProtocol, InputCommandCollection> ();
			_allProtocolCommands = new InputCommandCollection ();
			
			foreach (CommandInputHandlerExtension ext in AddinManager.GetExtensionNodes ("/Galaxium/CommandInputHandlers"))
			{
				ICommandInputHandler handler = ext.CommandInputHandler;
				if (handler == null)
					continue;
				
				foreach (InputCommand command in handler.SupportedCommands)
				{
					if (command.SupportsAllProtocols)
					{
						_allProtocolCommands.Add (command);
					}
					else
					{
						foreach (IProtocol protocol in command.Protocols)
						{
							if (!_protocolSpecificCommands.ContainsKey (protocol))
								_protocolSpecificCommands.Add (protocol, new InputCommandCollection ());
							
							InputCommandCollection collection = _protocolSpecificCommands[protocol];
							collection.Add (command);
						}
					}
				}
			}
		}
		
		public static IEnumerable<ICommandInputHandler> GetCommandInputHandlers (string command)
		{
			if (command == null)
				throw new ArgumentNullException ("command");
			
			foreach (InputCommand ic in _allProtocolCommands.GetCommands (command))
				yield return ic.CommandHandler;
		}
		
		public static IEnumerable<ICommandInputHandler> GetCommandInputHandlers (IProtocol protocol, string command)
		{
			if (protocol == null)
				throw new ArgumentNullException ("protocol");
			if (command == null)
				throw new ArgumentNullException ("command");
			
			if (!_protocolSpecificCommands.ContainsKey (protocol))
				yield break;
			
			List<InputCommand> commands = _protocolSpecificCommands[protocol].GetCommands (command);
			
			if (commands.Count == 0)
			{
				//no protocol specific commands, return protocol agnostic handlers
				foreach (InputCommand ic in _allProtocolCommands.GetCommands (command))
					yield return ic.CommandHandler;
			}
			else
			{
				foreach (InputCommand ic in commands)
					yield return ic.CommandHandler;
			}
		}

		//TODO: GetAllCommands ([protocol])
		//TODO: GetCommand (cmd [, protocol])
		
		public static bool HandleCommand (object sender, ISession session, IConversation conversation, string input, out string error)
		{
			if (session == null)
				throw new ArgumentNullException ("session");
			
			//TODO: This shouldn't really be necessary, what if its a command outside of a conversation?
			//if (conversation == null)
				//throw new ArgumentNullException ("conversation");
			
			if (input == null)
				throw new ArgumentNullException ("input");
			
			if (input.StartsWith ("/"))
				input = input.Substring (1);
			
			string command = null;
			string parameter = null;
			
			int spaceIndex = input.IndexOf (' ');
			
			if (spaceIndex < 0)
			{
				command = input;
				parameter = String.Empty;
			}
			else
			{
				command = input.Substring (0, spaceIndex);
				parameter = input.Substring (spaceIndex + 1);
			}
			
			List<ICommandInputHandler> handlers = new List<ICommandInputHandler> ();
			handlers.AddRange (GetCommandInputHandlers (session.Account.Protocol, command));
			
			if (handlers.Count == 0)
			{
				error = GettextCatalog.GetString ("Not a recognized command: {0}", command);
				return false;
			}
			else if (handlers.Count == 1)
			{
				return handlers[0].HandleCommand (sender, session, conversation, command, parameter, out error);
			}
			else
			{
				string currentError = null;
				StringBuilder sb = new StringBuilder ();

				foreach (ICommandInputHandler handler in handlers)
				{
					if (!handler.HandleCommand (sender, session, conversation, command, parameter, out currentError))
					{
						if (String.IsNullOrEmpty (currentError))
							continue;
						
						if (sb.Length > 0)
							sb.Append (" ,");
						
						sb.Append (currentError);
					}
				}
				
				error = sb.ToString ();
				
				return sb.Length == 0;
			}
		}
	}
}